Modelo

source("../../lib/som-utils.R")

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
source("../../lib/maps-utils.R")
Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1

Carga del modelo desde disco

mpr.set_base_path_analysis()
model <- mpr.load_model("som-333.rds.xz")
summary(model)
SOM of size 25x25 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 94881 objects.
Mean distance to the closest unit in the map: 0.068.
plot(model, type="changes")

Carga del dataset de entrada

df <- mpr.load_data("datos_mes.csv.xz")
df
summary(df)
 id_estacion           fecha             fecha_cnt           tmax      
 Length:94881       Length:94881       Min.   : 1.000   Min.   :-53.0  
 Class :character   Class :character   1st Qu.: 4.000   1st Qu.:148.0  
 Mode  :character   Mode  :character   Median : 6.000   Median :198.0  
                                       Mean   : 6.497   Mean   :200.2  
                                       3rd Qu.: 9.000   3rd Qu.:255.0  
                                       Max.   :12.000   Max.   :403.0  
      tmin             precip           nevada           prof_nieve      
 Min.   :-121.00   Min.   :  0.00   Min.   :0.000000   Min.   :   0.000  
 1st Qu.:  53.00   1st Qu.:  3.00   1st Qu.:0.000000   1st Qu.:   0.000  
 Median :  98.00   Median : 10.00   Median :0.000000   Median :   0.000  
 Mean   :  98.86   Mean   : 16.25   Mean   :0.000295   Mean   :   0.467  
 3rd Qu.: 148.00   3rd Qu.: 22.00   3rd Qu.:0.000000   3rd Qu.:   0.000  
 Max.   : 254.00   Max.   :422.00   Max.   :6.000000   Max.   :1834.000  
    longitud        latitud            altitud      
 Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:38.28   1st Qu.: -5.6417   1st Qu.:  42.0  
 Median :40.82   Median : -3.4500   Median : 247.0  
 Mean   :39.66   Mean   : -3.4350   Mean   : 418.5  
 3rd Qu.:42.08   3rd Qu.:  0.4914   3rd Qu.: 656.0  
 Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  

Carga de los mapas

world <- ne_countries(scale = "medium", returnclass = "sf")
spain <- subset(world, admin == "Spain")

Mapa de densidad

plot(model, type="count", shape = "straight", palette.name = mpr.degrade.bleu)

NĂºmero de elementos en cada celda:

nb <- table(model$unit.classif)
print(nb)

  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
 93 155 225 135 151 178 135 104 151 115 100 207 248 149 221 161 208 120 144 235 
 21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
176 233 279 187  64 164 233 220 158 261 145 129 149  89 153 225 203 216 197 156 
 41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
112 188 219 150 192 202 255 188 251 123 144 218 173 222  90 209 119 192 145 201 
 61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
121  84 164  70 113 156 164 179 171  61 183 236 171 167  46  67 113 174 112 172 
 81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
181 196 114 163 174 144 227 157 186 157 200 203 159 138 186 161 174 112 152  65 
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
 92 208 171 198 166 158 167 147 159  85 165  99 155 129 218 204 145 120  88 171 
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
144 157 163  95  70  62 214 243 199 187 231 206 112 131 102 186 218  98 161 177 
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
156 168 165 137 167 152 161  80 133  57  89 120 142 142 125 175 143 173 187 108 
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
200 144 166 188 184 147 167 180 112 153 177 131 136  74  43 184 204 108 165  92 
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
 97 121 214 181 171 132 140 189 245 168 132 122 135  73 118 208  71 169  87  60 
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
149 261 251 234 178 153 188 207 160 124 178 121 201 118  51 144 158 141 154 163 
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 
122 193 161 104  24 164 232 215 128 107  71 177 148  97 116 141 129 146 156 148 
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 
165 130 137 153 126 104 141 156 149 139 271 177 161 202 146 121 140  95  78 102 
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 
 59 131  92  97 146 149 160 130  34 153 114 109  84 104  58 171 297 101 247 207 
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 
125 143  96  79  76  74 127 145 116 156  50  65  83 108  74  52  95  40  53  17 
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 
186 182 235 151 181 164 190 150  90  90 103 130 188 142 169  82  98 111  84  44 
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 
 56  56  38   8   8 128 102 256 234 163 229 163 174 168 151  88 164 149 141  82 
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 
114 175 100  97 103 112 121  82  53  23 259 186 201 173 176 162 124 206 147 180 
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 
120 172 130 182 196 151 130 147  75 129 113 159 103  84  63 217 250 157 181 146 
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 
174 131 194 213 215 128 129 144 149  89 199 152 128 134 127 149 120 147  85  15 
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 
122 122 140 185 212  76 161 133 222 210 126  33 176 173 190 171 195  78 204 119 
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 
133 200 214  42  54 185 103 163 105 244 240 170 129 197 140 202  87  98 180 173 
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 
214 128 172  83 155 149 139 175 114  71 299 160 138  97 197 164 139 121 188 139 
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 
146 128 140 160 194 134 123  63 110 175 183 101 158  72  63 142 165 166 100 180 
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 
182 148 177 174 209 175 131 153 145 154 180 105  67  69 149 188 211 104  95  78 
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 
193 133 158 108 184 152 171 155 207 194 165 178 162 175 223 167 153  66  66 165 
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 
230 217 115  99  64 164 114 180 151 176 106 108 183 216 207 153 178 188 151 227 
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 
188  49 113 209 163 223 191 112 215 105 116 235 147 161 207 116 244  88 197 215 
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 
188 154 174 192 196 153 114  69 214 195 226 179 195 201 102  86 179 116 182 254 
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 
248 149 137 212 235 198 184 207 150 304 173 233 162 219 240 252 228 234 206 204 
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 
145 183 152 175 197 174 102 108 241 215 212 180 214 219 217 184 202  90 153 222 
621 622 623 624 625 
185 193 162 191 100 

ComprobaciĂ³n de nodos vacĂ­os:

dim_model <- 25*25;
len_nb = length(nb);
empty_nodes <- dim_model != len_nb;
if (empty_nodes) {
  print(paste("[Warning] Existen nodos vacĂ­os: ", len_nb, "/", dim_model))
}

Mapa de distancia entre vecinos

plot(model, type="dist.neighbours", shape = "straight")

Influencia de las variables

model_colnames = c("fecha_cnt", "tmax", "tmin", "precip")
model_ncol = length(model_colnames)

Mapa de variables.

plot(model, shape = "straight")

Mapa de calor por variable

par(mfrow=c(3,4))
for (j in 1:model_ncol) {
  plot(model, type="property", property=getCodes(model,1)[,j],
    palette.name=mpr.coolBlueHotRed,
    main=model_colnames[j],
    cex=0.5, shape = "straight")
}

CorrelaciĂ³n para cada columna del vector de nodos

if (!empty_nodes) {
  cor <- apply(getCodes(model,1), 2, mpr.weighted.correlation, w=nb, som=model)
  print(cor)
}
       fecha_cnt       tmax       tmin     precip
[1,]  0.04051879 -0.8059505 -0.7628205 0.59087433
[2,] -0.90812421 -0.2942793 -0.3247848 0.01056791

RepresentaciĂ³n de cada variable en un mapa de factores:

if (!empty_nodes) {
  par(mfrow=c(1,1))
  plot(cor[1,], cor[2,], xlim=c(-1,1), ylim=c(-1,1), type="n")
  lines(c(-1,1),c(0,0))
  lines(c(0,0),c(-1,1))
  text(cor[1,], cor[2,], labels=model_colnames, cex=0.75)
  symbols(0,0,circles=1,inches=F,add=T)
}

Importancia de cada variable - varianza ponderada por el tamaño de la celda:

if (!empty_nodes) {
  sigma2 <- sqrt(apply(getCodes(model,1),2,function(x,effectif)
     {m<-sum(effectif*(x-weighted.mean(x,effectif))^2)/(sum(effectif)-1)},
     effectif=nb))
  print(sort(sigma2,decreasing=T))
}
fecha_cnt      tmin      tmax    precip 
0.9940945 0.9912901 0.9911788 0.9884013 

Clustering

if (!empty_nodes) {
  hac <- mpr.hac(model, nb)
}

VisualizaciĂ³n de 3 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=3)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=3)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:248.0   1st Qu.:139.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :274.0   Median :161.0   Median : 4.000   Median :0  
 Mean   : 7.591   Mean   :275.1   Mean   :162.3   Mean   : 7.355   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:300.0   3rd Qu.:186.0   3rd Qu.:11.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :58.000   Max.   :0  
   prof_nieve        longitud        latitud            altitud      
 Min.   : 0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.000   1st Qu.:37.28   1st Qu.: -5.7333   1st Qu.:  32.0  
 Median : 0.000   Median :39.99   Median : -3.5556   Median :  91.0  
 Mean   : 0.002   Mean   :38.80   Mean   : -3.8393   Mean   : 300.2  
 3rd Qu.: 0.000   3rd Qu.:41.65   3rd Qu.:  0.4731   3rd Qu.: 554.0  
 Max.   :35.000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin             precip      
 Min.   : 1.00   Min.   :-42.0   Min.   :-110.00   Min.   :  0.00  
 1st Qu.:10.00   1st Qu.:123.0   1st Qu.:  37.00   1st Qu.:  7.00  
 Median :11.00   Median :160.0   Median :  70.00   Median : 19.00  
 Mean   :10.67   Mean   :157.1   Mean   :  67.88   Mean   : 27.27  
 3rd Qu.:12.00   3rd Qu.:193.0   3rd Qu.:  99.00   3rd Qu.: 36.00  
 Max.   :12.00   Max.   :350.0   Max.   : 223.00   Max.   :422.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:39.49   1st Qu.: -5.5975  
 Median :0.0000000   Median :  0.0000   Median :41.29   Median : -2.9056  
 Mean   :0.0003915   Mean   :  0.6095   Mean   :40.50   Mean   : -2.9650  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:42.39   3rd Qu.:  0.4942  
 Max.   :3.0000000   Max.   :892.0000   Max.   :43.57   Max.   :  4.2156  
    altitud    
 Min.   :   1  
 1st Qu.:  64  
 Median : 370  
 Mean   : 521  
 3rd Qu.: 750  
 Max.   :2535  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.0   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:130.0   1st Qu.:  31.0   1st Qu.: 6.00  
 Median :3.000   Median :164.0   Median :  64.0   Median :13.00  
 Mean   :3.042   Mean   :160.6   Mean   :  62.1   Mean   :17.41  
 3rd Qu.:4.000   3rd Qu.:196.0   3rd Qu.:  94.0   3rd Qu.:25.00  
 Max.   :8.000   Max.   :281.0   Max.   : 185.0   Max.   :93.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.6417  
 Median :0.000000   Median :   0.0000   Median :40.96   Median : -3.1742  
 Mean   :0.000495   Mean   :   0.7872   Mean   :39.91   Mean   : -3.3638  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.24   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 316.0  
 Mean   : 460.5  
 3rd Qu.: 687.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

VisualizaciĂ³n de 4 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=4)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=4)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:248.0   1st Qu.:139.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :274.0   Median :161.0   Median : 4.000   Median :0  
 Mean   : 7.591   Mean   :275.1   Mean   :162.3   Mean   : 7.355   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:300.0   3rd Qu.:186.0   3rd Qu.:11.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :58.000   Max.   :0  
   prof_nieve        longitud        latitud            altitud      
 Min.   : 0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.000   1st Qu.:37.28   1st Qu.: -5.7333   1st Qu.:  32.0  
 Median : 0.000   Median :39.99   Median : -3.5556   Median :  91.0  
 Mean   : 0.002   Mean   :38.80   Mean   : -3.8393   Mean   : 300.2  
 3rd Qu.: 0.000   3rd Qu.:41.65   3rd Qu.:  0.4731   3rd Qu.: 554.0  
 Max.   :35.000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax          tmin             precip     
 Min.   : 7.00   Min.   :-24   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:125   1st Qu.:  35.00   1st Qu.: 6.00  
 Median :11.00   Median :164   Median :  69.00   Median :14.00  
 Mean   :10.91   Mean   :159   Mean   :  66.01   Mean   :16.97  
 3rd Qu.:12.00   3rd Qu.:194   3rd Qu.:  98.00   3rd Qu.:26.00  
 Max.   :12.00   Max.   :275   Max.   : 204.00   Max.   :66.00  
     nevada            prof_nieve         longitud        latitud        
 Min.   :0.0000000   Min.   :  0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.000   1st Qu.:39.47   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.000   Median :41.04   Median : -2.4831  
 Mean   :0.0004743   Mean   :  0.123   Mean   :40.31   Mean   : -2.7524  
 3rd Qu.:0.0000000   3rd Qu.:  0.000   3rd Qu.:42.12   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :124.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  68.6  
 Median : 442.0  
 Mean   : 529.5  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 25.00  
 1st Qu.: 9.000   1st Qu.:118.0   1st Qu.:  50.00   1st Qu.: 54.00  
 Median :11.000   Median :145.0   Median :  76.00   Median : 68.00  
 Mean   : 9.566   Mean   :148.2   Mean   :  76.74   Mean   : 76.05  
 3rd Qu.:12.000   3rd Qu.:185.0   3rd Qu.: 111.00   3rd Qu.: 90.00  
 Max.   :12.000   Max.   :350.0   Max.   : 223.00   Max.   :422.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.456  
 Median :0   Median :  0.000   Median :42.43   Median : -3.831  
 Mean   :0   Mean   :  2.912   Mean   :41.40   Mean   : -3.971  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.169  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 200.0  
 Mean   : 480.8  
 3rd Qu.: 510.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.0   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:130.0   1st Qu.:  31.0   1st Qu.: 6.00  
 Median :3.000   Median :164.0   Median :  64.0   Median :13.00  
 Mean   :3.042   Mean   :160.6   Mean   :  62.1   Mean   :17.41  
 3rd Qu.:4.000   3rd Qu.:196.0   3rd Qu.:  94.0   3rd Qu.:25.00  
 Max.   :8.000   Max.   :281.0   Max.   : 185.0   Max.   :93.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.6417  
 Median :0.000000   Median :   0.0000   Median :40.96   Median : -3.1742  
 Mean   :0.000495   Mean   :   0.7872   Mean   :39.91   Mean   : -3.3638  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.24   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 316.0  
 Mean   : 460.5  
 3rd Qu.: 687.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

VisualizaciĂ³n de 5 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=5)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=5)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:248.0   1st Qu.:139.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :274.0   Median :161.0   Median : 4.000   Median :0  
 Mean   : 7.591   Mean   :275.1   Mean   :162.3   Mean   : 7.355   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:300.0   3rd Qu.:186.0   3rd Qu.:11.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :58.000   Max.   :0  
   prof_nieve        longitud        latitud            altitud      
 Min.   : 0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.000   1st Qu.:37.28   1st Qu.: -5.7333   1st Qu.:  32.0  
 Median : 0.000   Median :39.99   Median : -3.5556   Median :  91.0  
 Mean   : 0.002   Mean   :38.80   Mean   : -3.8393   Mean   : 300.2  
 3rd Qu.: 0.000   3rd Qu.:41.65   3rd Qu.:  0.4731   3rd Qu.: 554.0  
 Max.   :35.000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax          tmin             precip     
 Min.   : 7.00   Min.   :-24   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:125   1st Qu.:  35.00   1st Qu.: 6.00  
 Median :11.00   Median :164   Median :  69.00   Median :14.00  
 Mean   :10.91   Mean   :159   Mean   :  66.01   Mean   :16.97  
 3rd Qu.:12.00   3rd Qu.:194   3rd Qu.:  98.00   3rd Qu.:26.00  
 Max.   :12.00   Max.   :275   Max.   : 204.00   Max.   :66.00  
     nevada            prof_nieve         longitud        latitud        
 Min.   :0.0000000   Min.   :  0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.000   1st Qu.:39.47   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.000   Median :41.04   Median : -2.4831  
 Mean   :0.0004743   Mean   :  0.123   Mean   :40.31   Mean   : -2.7524  
 3rd Qu.:0.0000000   3rd Qu.:  0.000   3rd Qu.:42.12   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :124.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  68.6  
 Median : 442.0  
 Mean   : 529.5  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 25.00  
 1st Qu.: 9.000   1st Qu.:118.0   1st Qu.:  50.00   1st Qu.: 54.00  
 Median :11.000   Median :145.0   Median :  75.00   Median : 67.00  
 Mean   : 9.609   Mean   :148.1   Mean   :  76.56   Mean   : 73.97  
 3rd Qu.:12.000   3rd Qu.:185.0   3rd Qu.: 111.00   3rd Qu.: 89.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :181.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.456  
 Median :0   Median :  0.000   Median :42.43   Median : -3.831  
 Mean   :0   Mean   :  2.799   Mean   :41.41   Mean   : -3.930  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.033  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 200.0  
 Mean   : 481.7  
 3rd Qu.: 510.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :155.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:189.2   1st Qu.:0  
 Median : 7.000   Median :135.5   Median : 81.00   Median :209.0   Median :0  
 Mean   : 6.554   Mean   :155.7   Mean   : 89.07   Mean   :223.0   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:168.0   3rd Qu.: 98.50   3rd Qu.:234.2   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0.00   1st Qu.:40.13   1st Qu.: -8.624   1st Qu.:  68.6  
 Median :  0.00   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10.89   Mean   :40.54   Mean   : -6.897   Mean   : 417.0  
 3rd Qu.:  0.00   3rd Qu.:42.55   3rd Qu.: -3.801   3rd Qu.: 370.0  
 Max.   :607.00   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.0   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:130.0   1st Qu.:  31.0   1st Qu.: 6.00  
 Median :3.000   Median :164.0   Median :  64.0   Median :13.00  
 Mean   :3.042   Mean   :160.6   Mean   :  62.1   Mean   :17.41  
 3rd Qu.:4.000   3rd Qu.:196.0   3rd Qu.:  94.0   3rd Qu.:25.00  
 Max.   :8.000   Max.   :281.0   Max.   : 185.0   Max.   :93.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.6417  
 Median :0.000000   Median :   0.0000   Median :40.96   Median : -3.1742  
 Mean   :0.000495   Mean   :   0.7872   Mean   :39.91   Mean   : -3.3638  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.24   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 316.0  
 Mean   : 460.5  
 3rd Qu.: 687.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

VisualizaciĂ³n de 6 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=6)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=6)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:248.0   1st Qu.:139.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :274.0   Median :161.0   Median : 4.000   Median :0  
 Mean   : 7.591   Mean   :275.1   Mean   :162.3   Mean   : 7.355   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:300.0   3rd Qu.:186.0   3rd Qu.:11.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :58.000   Max.   :0  
   prof_nieve        longitud        latitud            altitud      
 Min.   : 0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.000   1st Qu.:37.28   1st Qu.: -5.7333   1st Qu.:  32.0  
 Median : 0.000   Median :39.99   Median : -3.5556   Median :  91.0  
 Mean   : 0.002   Mean   :38.80   Mean   : -3.8393   Mean   : 300.2  
 3rd Qu.: 0.000   3rd Qu.:41.65   3rd Qu.:  0.4731   3rd Qu.: 554.0  
 Max.   :35.000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax          tmin             precip     
 Min.   : 7.00   Min.   :-24   Min.   :-110.00   Min.   : 0.00  
 1st Qu.:10.00   1st Qu.:125   1st Qu.:  35.00   1st Qu.: 6.00  
 Median :11.00   Median :164   Median :  69.00   Median :14.00  
 Mean   :10.91   Mean   :159   Mean   :  66.01   Mean   :16.97  
 3rd Qu.:12.00   3rd Qu.:194   3rd Qu.:  98.00   3rd Qu.:26.00  
 Max.   :12.00   Max.   :275   Max.   : 204.00   Max.   :66.00  
     nevada            prof_nieve         longitud        latitud        
 Min.   :0.0000000   Min.   :  0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.000   1st Qu.:39.47   1st Qu.: -4.8500  
 Median :0.0000000   Median :  0.000   Median :41.04   Median : -2.4831  
 Mean   :0.0004743   Mean   :  0.123   Mean   :40.31   Mean   : -2.7524  
 3rd Qu.:0.0000000   3rd Qu.:  0.000   3rd Qu.:42.12   3rd Qu.:  0.5706  
 Max.   :3.0000000   Max.   :124.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  68.6  
 Median : 442.0  
 Mean   : 529.5  
 3rd Qu.: 779.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 25.00  
 1st Qu.: 9.000   1st Qu.:118.0   1st Qu.:  50.00   1st Qu.: 54.00  
 Median :11.000   Median :145.0   Median :  75.00   Median : 67.00  
 Mean   : 9.609   Mean   :148.1   Mean   :  76.56   Mean   : 73.97  
 3rd Qu.:12.000   3rd Qu.:185.0   3rd Qu.: 111.00   3rd Qu.: 89.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :181.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.456  
 Median :0   Median :  0.000   Median :42.43   Median : -3.831  
 Mean   :0   Mean   :  2.799   Mean   :41.41   Mean   : -3.930  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.033  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 200.0  
 Mean   : 481.7  
 3rd Qu.: 510.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :155.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:189.2   1st Qu.:0  
 Median : 7.000   Median :135.5   Median : 81.00   Median :209.0   Median :0  
 Mean   : 6.554   Mean   :155.7   Mean   : 89.07   Mean   :223.0   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:168.0   3rd Qu.: 98.50   3rd Qu.:234.2   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0.00   1st Qu.:40.13   1st Qu.: -8.624   1st Qu.:  68.6  
 Median :  0.00   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10.89   Mean   :40.54   Mean   : -6.897   Mean   : 417.0  
 3rd Qu.:  0.00   3rd Qu.:42.55   3rd Qu.: -3.801   3rd Qu.: 370.0  
 Max.   :607.00   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin           precip          nevada 
 Min.   :1.000   Min.   : 87.0   Min.   : 25.0   Min.   : 0.00   Min.   :0  
 1st Qu.:4.000   1st Qu.:193.0   1st Qu.: 85.0   1st Qu.: 6.00   1st Qu.:0  
 Median :4.000   Median :209.0   Median :102.0   Median :12.00   Median :0  
 Mean   :4.292   Mean   :208.6   Mean   :104.9   Mean   :12.43   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:225.0   3rd Qu.:122.0   3rd Qu.:18.00   3rd Qu.:0  
 Max.   :7.000   Max.   :281.0   Max.   :185.0   Max.   :41.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.18   1st Qu.: -6.3392   1st Qu.:  32.0  
 Median : 0.00000   Median :40.35   Median : -3.7892   Median :  91.0  
 Mean   : 0.02027   Mean   :38.51   Mean   : -4.6440   Mean   : 326.7  
 3rd Qu.: 0.00000   3rd Qu.:41.70   3rd Qu.:  0.0714   3rd Qu.: 609.0  
 Max.   :38.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2451.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin             precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.:1.000   1st Qu.:115.0   1st Qu.:  18.00   1st Qu.: 5.00  
 Median :2.000   Median :145.0   Median :  45.00   Median :15.00  
 Mean   :2.484   Mean   :139.2   Mean   :  43.02   Mean   :19.63  
 3rd Qu.:3.000   3rd Qu.:168.0   3rd Qu.:  70.00   3rd Qu.:29.00  
 Max.   :8.000   Max.   :259.0   Max.   : 177.00   Max.   :93.00  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:39.49   1st Qu.: -5.2892  
 Median :0.000000   Median :   0.000   Median :41.17   Median : -2.7331  
 Mean   :0.000715   Mean   :   1.129   Mean   :40.54   Mean   : -2.7929  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:42.36   3rd Qu.:  0.4942  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  68.6  
 Median : 412.0  
 Mean   : 520.1  
 3rd Qu.: 775.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

VisualizaciĂ³n de 8 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=8)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=8)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:248.0   1st Qu.:139.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :274.0   Median :161.0   Median : 4.000   Median :0  
 Mean   : 7.591   Mean   :275.1   Mean   :162.3   Mean   : 7.355   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:300.0   3rd Qu.:186.0   3rd Qu.:11.000   3rd Qu.:0  
 Max.   :12.000   Max.   :403.0   Max.   :254.0   Max.   :58.000   Max.   :0  
   prof_nieve        longitud        latitud            altitud      
 Min.   : 0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.000   1st Qu.:37.28   1st Qu.: -5.7333   1st Qu.:  32.0  
 Median : 0.000   Median :39.99   Median : -3.5556   Median :  91.0  
 Mean   : 0.002   Mean   :38.80   Mean   : -3.8393   Mean   : 300.2  
 3rd Qu.: 0.000   3rd Qu.:41.65   3rd Qu.:  0.4731   3rd Qu.: 554.0  
 Max.   :35.000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   :115.0   Min.   :  4.00   Min.   : 0.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:169.0   1st Qu.: 74.00   1st Qu.: 6.00   1st Qu.:0  
 Median :10.00   Median :188.0   Median : 91.00   Median :17.00   Median :0  
 Mean   :10.51   Mean   :188.8   Mean   : 93.33   Mean   :18.88   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:208.0   3rd Qu.:110.00   3rd Qu.:29.00   3rd Qu.:0  
 Max.   :12.00   Max.   :275.0   Max.   :204.00   Max.   :66.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:38.28   1st Qu.: -5.6000   1st Qu.:  35.0  
 Median : 0.00000   Median :40.84   Median : -2.4831   Median : 143.0  
 Mean   : 0.02879   Mean   :39.99   Mean   : -2.8337   Mean   : 393.6  
 3rd Qu.: 0.00000   3rd Qu.:42.12   3rd Qu.:  0.5706   3rd Qu.: 628.0  
 Max.   :59.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin          precip     
 Min.   : 8.00   Min.   :-24.0   Min.   :-110   Min.   : 0.00  
 1st Qu.:11.00   1st Qu.: 95.0   1st Qu.:   7   1st Qu.: 5.00  
 Median :12.00   Median :115.0   Median :  27   Median :12.00  
 Mean   :11.52   Mean   :113.1   Mean   :  24   Mean   :14.03  
 3rd Qu.:12.00   3rd Qu.:134.0   3rd Qu.:  43   3rd Qu.:21.00  
 Max.   :12.00   Max.   :192.0   Max.   :  83   Max.   :47.00  
     nevada           prof_nieve         longitud        latitud        
 Min.   :0.000000   Min.   :  0.000   Min.   :28.31   Min.   :-16.4992  
 1st Qu.:0.000000   1st Qu.:  0.000   1st Qu.:40.41   1st Qu.: -4.1269  
 Median :0.000000   Median :  0.000   Median :41.39   Median : -2.6544  
 Mean   :0.001203   Mean   :  0.268   Mean   :40.81   Mean   : -2.6274  
 3rd Qu.:0.000000   3rd Qu.:  0.000   3rd Qu.:42.12   3rd Qu.:  0.4914  
 Max.   :3.000000   Max.   :124.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.: 412.0  
 Median : 656.0  
 Mean   : 738.5  
 3rd Qu.: 900.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 25.00  
 1st Qu.: 9.000   1st Qu.:118.0   1st Qu.:  50.00   1st Qu.: 54.00  
 Median :11.000   Median :145.0   Median :  75.00   Median : 67.00  
 Mean   : 9.609   Mean   :148.1   Mean   :  76.56   Mean   : 73.97  
 3rd Qu.:12.000   3rd Qu.:185.0   3rd Qu.: 111.00   3rd Qu.: 89.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :181.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.456  
 Median :0   Median :  0.000   Median :42.43   Median : -3.831  
 Mean   :0   Mean   :  2.799   Mean   :41.41   Mean   : -3.930  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.033  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 200.0  
 Mean   : 481.7  
 3rd Qu.: 510.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :155.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:189.2   1st Qu.:0  
 Median : 7.000   Median :135.5   Median : 81.00   Median :209.0   Median :0  
 Mean   : 6.554   Mean   :155.7   Mean   : 89.07   Mean   :223.0   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:168.0   3rd Qu.: 98.50   3rd Qu.:234.2   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0.00   1st Qu.:40.13   1st Qu.: -8.624   1st Qu.:  68.6  
 Median :  0.00   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10.89   Mean   :40.54   Mean   : -6.897   Mean   : 417.0  
 3rd Qu.:  0.00   3rd Qu.:42.55   3rd Qu.: -3.801   3rd Qu.: 370.0  
 Max.   :607.00   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin           precip          nevada 
 Min.   :1.000   Min.   : 87.0   Min.   : 25.0   Min.   : 0.00   Min.   :0  
 1st Qu.:4.000   1st Qu.:193.0   1st Qu.: 85.0   1st Qu.: 6.00   1st Qu.:0  
 Median :4.000   Median :209.0   Median :102.0   Median :12.00   Median :0  
 Mean   :4.292   Mean   :208.6   Mean   :104.9   Mean   :12.43   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:225.0   3rd Qu.:122.0   3rd Qu.:18.00   3rd Qu.:0  
 Max.   :7.000   Max.   :281.0   Max.   :185.0   Max.   :41.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.18   1st Qu.: -6.3392   1st Qu.:  32.0  
 Median : 0.00000   Median :40.35   Median : -3.7892   Median :  91.0  
 Mean   : 0.02027   Mean   :38.51   Mean   : -4.6440   Mean   : 326.7  
 3rd Qu.: 0.00000   3rd Qu.:41.70   3rd Qu.:  0.0714   3rd Qu.: 609.0  
 Max.   :38.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2451.0  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt          tmax            tmin           precip          nevada 
 Min.   :1.000   Min.   : 45.0   Min.   :-41.0   Min.   :13.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:128.0   1st Qu.: 42.0   1st Qu.:26.00   1st Qu.:0  
 Median :3.000   Median :151.0   Median : 62.0   Median :35.00   Median :0  
 Mean   :3.145   Mean   :149.9   Mean   : 62.1   Mean   :37.49   Mean   :0  
 3rd Qu.:4.000   3rd Qu.:173.0   3rd Qu.: 82.0   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :8.000   Max.   :259.0   Max.   :177.0   Max.   :93.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:40.47   1st Qu.: -5.8728   1st Qu.:  58.0  
 Median :  0.0000   Median :42.08   Median : -3.7225   Median : 252.0  
 Mean   :  0.4189   Mean   :41.19   Mean   : -3.3349   Mean   : 433.9  
 3rd Qu.:  0.0000   3rd Qu.:43.12   3rd Qu.:  0.3264   3rd Qu.: 628.0  
 Max.   :666.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt          tmax            tmin             precip      
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.000  
 1st Qu.:1.000   1st Qu.:107.0   1st Qu.:   7.00   1st Qu.: 3.000  
 Median :2.000   Median :141.0   Median :  34.00   Median : 8.000  
 Mean   :2.116   Mean   :133.3   Mean   :  32.41   Mean   : 9.684  
 3rd Qu.:3.000   3rd Qu.:165.0   3rd Qu.:  59.00   3rd Qu.:14.000  
 Max.   :7.000   Max.   :254.0   Max.   : 140.00   Max.   :74.000  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :28.31   Min.   :-17.7550  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:38.99   1st Qu.: -4.6800  
 Median :0.000000   Median :   0.000   Median :40.93   Median : -2.3308  
 Mean   :0.001114   Mean   :   1.525   Mean   :40.17   Mean   : -2.4912  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:41.77   3rd Qu.:  0.5706  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  81.0  
 Median : 515.0  
 Mean   : 568.1  
 3rd Qu.: 790.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

VisualizaciĂ³n de 10 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=10)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=10)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)
  df.cluster09 <- subset(df, cluster==9)
  df.cluster10 <- subset(df, cluster==10)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster09 <- select(df.cluster09, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster10 <- select(df.cluster10, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 5.000   Min.   :218.0   Min.   :111.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 7.000   1st Qu.:288.0   1st Qu.:171.0   1st Qu.: 0.000   1st Qu.:0  
 Median : 8.000   Median :305.0   Median :189.0   Median : 1.000   Median :0  
 Mean   : 7.755   Mean   :306.4   Mean   :187.8   Mean   : 3.299   Mean   :0  
 3rd Qu.: 8.000   3rd Qu.:324.0   3rd Qu.:205.0   3rd Qu.: 5.000   3rd Qu.:0  
 Max.   :11.000   Max.   :403.0   Max.   :254.0   Max.   :25.000   Max.   :0  
   prof_nieve    longitud        latitud            altitud      
 Min.   :0    Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:0    1st Qu.:37.18   1st Qu.: -5.6156   1st Qu.:  27.0  
 Median :0    Median :38.95   Median : -3.1742   Median :  81.0  
 Mean   :0    Mean   :38.08   Mean   : -3.5389   Mean   : 223.4  
 3rd Qu.:0    3rd Qu.:40.72   3rd Qu.:  0.4914   3rd Qu.: 446.0  
 Max.   :0    Max.   :43.56   Max.   :  4.2156   Max.   :1167.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt           tmax            tmin           precip           nevada 
 Min.   : 3.000   Min.   :176.0   Min.   : 54.0   Min.   : 0.000   Min.   :0  
 1st Qu.: 6.000   1st Qu.:236.0   1st Qu.:128.0   1st Qu.: 2.000   1st Qu.:0  
 Median : 7.000   Median :256.0   Median :145.0   Median : 7.000   Median :0  
 Mean   : 7.488   Mean   :255.4   Mean   :146.1   Mean   : 9.916   Mean   :0  
 3rd Qu.: 9.000   3rd Qu.:275.0   3rd Qu.:163.0   3rd Qu.:15.000   3rd Qu.:0  
 Max.   :12.000   Max.   :352.0   Max.   :244.0   Max.   :58.000   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.79   1st Qu.: -5.8728   1st Qu.:  33.0  
 Median : 0.00000   Median :40.84   Median : -3.6781   Median : 143.0  
 Mean   : 0.00326   Mean   :39.26   Mean   : -4.0290   Mean   : 348.6  
 3rd Qu.: 0.00000   3rd Qu.:42.08   3rd Qu.:  0.3664   3rd Qu.: 627.0  
 Max.   :35.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   :115.0   Min.   :  4.00   Min.   : 0.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:169.0   1st Qu.: 74.00   1st Qu.: 6.00   1st Qu.:0  
 Median :10.00   Median :188.0   Median : 91.00   Median :17.00   Median :0  
 Mean   :10.51   Mean   :188.8   Mean   : 93.33   Mean   :18.88   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:208.0   3rd Qu.:110.00   3rd Qu.:29.00   3rd Qu.:0  
 Max.   :12.00   Max.   :275.0   Max.   :204.00   Max.   :66.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:38.28   1st Qu.: -5.6000   1st Qu.:  35.0  
 Median : 0.00000   Median :40.84   Median : -2.4831   Median : 143.0  
 Mean   : 0.02879   Mean   :39.99   Mean   : -2.8337   Mean   : 393.6  
 3rd Qu.: 0.00000   3rd Qu.:42.12   3rd Qu.:  0.5706   3rd Qu.: 628.0  
 Max.   :59.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt          tmax            tmin          precip     
 Min.   : 8.00   Min.   :-24.0   Min.   :-110   Min.   : 0.00  
 1st Qu.:11.00   1st Qu.: 95.0   1st Qu.:   7   1st Qu.: 5.00  
 Median :12.00   Median :115.0   Median :  27   Median :12.00  
 Mean   :11.52   Mean   :113.1   Mean   :  24   Mean   :14.03  
 3rd Qu.:12.00   3rd Qu.:134.0   3rd Qu.:  43   3rd Qu.:21.00  
 Max.   :12.00   Max.   :192.0   Max.   :  83   Max.   :47.00  
     nevada           prof_nieve         longitud        latitud        
 Min.   :0.000000   Min.   :  0.000   Min.   :28.31   Min.   :-16.4992  
 1st Qu.:0.000000   1st Qu.:  0.000   1st Qu.:40.41   1st Qu.: -4.1269  
 Median :0.000000   Median :  0.000   Median :41.39   Median : -2.6544  
 Mean   :0.001203   Mean   :  0.268   Mean   :40.81   Mean   : -2.6274  
 3rd Qu.:0.000000   3rd Qu.:  0.000   3rd Qu.:42.12   3rd Qu.:  0.4914  
 Max.   :3.000000   Max.   :124.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.: 412.0  
 Median : 656.0  
 Mean   : 738.5  
 3rd Qu.: 900.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 4.00   Min.   :-17.0   Min.   :-80.00   Min.   : 25.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:120.0   1st Qu.: 51.00   1st Qu.: 50.00   1st Qu.:0  
 Median :11.00   Median :148.0   Median : 78.00   Median : 60.00   Median :0  
 Mean   :10.68   Mean   :154.3   Mean   : 81.12   Mean   : 61.93   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:200.0   3rd Qu.:120.00   3rd Qu.: 72.00   3rd Qu.:0  
 Max.   :12.00   Max.   :336.0   Max.   :219.00   Max.   :138.00   Max.   :0  
   prof_nieve         longitud        latitud            altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -6.0556   1st Qu.:  42.0  
 Median :  0.000   Median :42.43   Median : -3.7994   Median : 143.0  
 Mean   :  1.158   Mean   :41.41   Mean   : -3.5259   Mean   : 475.4  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.:  0.3664   3rd Qu.: 521.0  
 Max.   :382.000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   : 1.00   Min.   :-42.0   Min.   :-108.00   Min.   : 68.0   Min.   :0  
 1st Qu.: 2.00   1st Qu.:111.0   1st Qu.:  45.00   1st Qu.: 94.0   1st Qu.:0  
 Median : 5.00   Median :135.5   Median :  70.00   Median :105.0   Median :0  
 Mean   : 6.44   Mean   :129.5   Mean   :  63.05   Mean   :109.7   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:159.0   3rd Qu.:  91.25   3rd Qu.:122.0   3rd Qu.:0  
 Max.   :12.00   Max.   :250.0   Max.   : 189.00   Max.   :181.0   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.00   1st Qu.:40.82   1st Qu.: -8.411   1st Qu.:  42.0  
 Median :  0.00   Median :42.44   Median : -5.598   Median : 251.0  
 Mean   :  7.67   Mean   :41.40   Mean   : -5.129   Mean   : 500.5  
 3rd Qu.:  0.00   3rd Qu.:43.16   3rd Qu.: -2.039   3rd Qu.: 370.0  
 Max.   :892.00   Max.   :43.57   Max.   :  3.035   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :155.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 63.00   1st Qu.:189.2   1st Qu.:0  
 Median : 7.000   Median :135.5   Median : 81.00   Median :209.0   Median :0  
 Mean   : 6.554   Mean   :155.7   Mean   : 89.07   Mean   :223.0   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:168.0   3rd Qu.: 98.50   3rd Qu.:234.2   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve        longitud        latitud           altitud      
 Min.   :  0.00   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0.00   1st Qu.:40.13   1st Qu.: -8.624   1st Qu.:  68.6  
 Median :  0.00   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10.89   Mean   :40.54   Mean   : -6.897   Mean   : 417.0  
 3rd Qu.:  0.00   3rd Qu.:42.55   3rd Qu.: -3.801   3rd Qu.: 370.0  
 Max.   :607.00   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt          tmax            tmin           precip          nevada 
 Min.   :1.000   Min.   : 87.0   Min.   : 25.0   Min.   : 0.00   Min.   :0  
 1st Qu.:4.000   1st Qu.:193.0   1st Qu.: 85.0   1st Qu.: 6.00   1st Qu.:0  
 Median :4.000   Median :209.0   Median :102.0   Median :12.00   Median :0  
 Mean   :4.292   Mean   :208.6   Mean   :104.9   Mean   :12.43   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:225.0   3rd Qu.:122.0   3rd Qu.:18.00   3rd Qu.:0  
 Max.   :7.000   Max.   :281.0   Max.   :185.0   Max.   :41.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.18   1st Qu.: -6.3392   1st Qu.:  32.0  
 Median : 0.00000   Median :40.35   Median : -3.7892   Median :  91.0  
 Mean   : 0.02027   Mean   :38.51   Mean   : -4.6440   Mean   : 326.7  
 3rd Qu.: 0.00000   3rd Qu.:41.70   3rd Qu.:  0.0714   3rd Qu.: 609.0  
 Max.   :38.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2451.0  
if (!empty_nodes) summary(df.cluster09)
   fecha_cnt          tmax            tmin           precip          nevada 
 Min.   :1.000   Min.   : 45.0   Min.   :-41.0   Min.   :13.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:128.0   1st Qu.: 42.0   1st Qu.:26.00   1st Qu.:0  
 Median :3.000   Median :151.0   Median : 62.0   Median :35.00   Median :0  
 Mean   :3.145   Mean   :149.9   Mean   : 62.1   Mean   :37.49   Mean   :0  
 3rd Qu.:4.000   3rd Qu.:173.0   3rd Qu.: 82.0   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :8.000   Max.   :259.0   Max.   :177.0   Max.   :93.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:40.47   1st Qu.: -5.8728   1st Qu.:  58.0  
 Median :  0.0000   Median :42.08   Median : -3.7225   Median : 252.0  
 Mean   :  0.4189   Mean   :41.19   Mean   : -3.3349   Mean   : 433.9  
 3rd Qu.:  0.0000   3rd Qu.:43.12   3rd Qu.:  0.3264   3rd Qu.: 628.0  
 Max.   :666.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster10)
   fecha_cnt          tmax            tmin             precip      
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.000  
 1st Qu.:1.000   1st Qu.:107.0   1st Qu.:   7.00   1st Qu.: 3.000  
 Median :2.000   Median :141.0   Median :  34.00   Median : 8.000  
 Mean   :2.116   Mean   :133.3   Mean   :  32.41   Mean   : 9.684  
 3rd Qu.:3.000   3rd Qu.:165.0   3rd Qu.:  59.00   3rd Qu.:14.000  
 Max.   :7.000   Max.   :254.0   Max.   : 140.00   Max.   :74.000  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :   0.000   Min.   :28.31   Min.   :-17.7550  
 1st Qu.:0.000000   1st Qu.:   0.000   1st Qu.:38.99   1st Qu.: -4.6800  
 Median :0.000000   Median :   0.000   Median :40.93   Median : -2.3308  
 Mean   :0.001114   Mean   :   1.525   Mean   :40.17   Mean   : -2.4912  
 3rd Qu.:0.000000   3rd Qu.:   0.000   3rd Qu.:41.77   3rd Qu.:  0.5706  
 Max.   :6.000000   Max.   :1834.000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  81.0  
 Median : 515.0  
 Mean   : 568.1  
 3rd Qu.: 790.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1], dim(df.cluster09)[1], dim(df.cluster10)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08", "cluster09", "cluster10"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.hist(df.cluster09)

if (!empty_nodes) mpr.hist(df.cluster10)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster09)

if (!empty_nodes) mpr.boxplot(df.cluster10)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
  df.cluster09.grouped <- mpr.group_by_geo(df.cluster09)
  df.cluster10.grouped <- mpr.group_by_geo(df.cluster10)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster09.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster10.grouped)

LS0tCnRpdGxlOiAiQW7DoWxpc2lzIGRlIG1vZGVsb3MgU09NIC0gRnJlY3VlbmNpYSBkYXRvcyBkZSBlbnRyYWRhOiBtZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTW9kZWxvCgoqIElEOiAzMzMKKiBEZXNjcmlwY2nDs246IAoqIEZyZWN1ZW5jaWE6IG1lcwoqIFZhcmlhYmxlczogZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAKKiBEaW1lbnNpb25lcyBkZWwgbWFwYTogMjUsMjUKKiBJdGVyYWNpb25lczogMTAwMAoqIFBhcsOhbWV0cm9zIGFkaWNpb25hbGVzOiAKCmBgYHtyfQpzb3VyY2UoIi4uLy4uL2xpYi9zb20tdXRpbHMuUiIpCnNvdXJjZSgiLi4vLi4vbGliL21hcHMtdXRpbHMuUiIpCmBgYAoKIyBDYXJnYSBkZWwgbW9kZWxvIGRlc2RlIGRpc2NvCgpgYGB7cn0KbXByLnNldF9iYXNlX3BhdGhfYW5hbHlzaXMoKQptb2RlbCA8LSBtcHIubG9hZF9tb2RlbCgic29tLTMzMy5yZHMueHoiKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJjaGFuZ2VzIikKYGBgCgojIENhcmdhIGRlbCBkYXRhc2V0IGRlIGVudHJhZGEKCmBgYHtyfQpkZiA8LSBtcHIubG9hZF9kYXRhKCJkYXRvc19tZXMuY3N2Lnh6IikKYGBgCgpgYGB7cn0KZGYKYGBgCgpgYGB7cn0Kc3VtbWFyeShkZikKYGBgCgojIENhcmdhIGRlIGxvcyBtYXBhcwoKYGBge3J9CndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpCnNwYWluIDwtIHN1YnNldCh3b3JsZCwgYWRtaW4gPT0gIlNwYWluIikKYGBgCgojIE1hcGEgZGUgZGVuc2lkYWQKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJjb3VudCIsIHNoYXBlID0gInN0cmFpZ2h0IiwgcGFsZXR0ZS5uYW1lID0gbXByLmRlZ3JhZGUuYmxldSkKYGBgCgpOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNlbGRhOgoKYGBge3J9Cm5iIDwtIHRhYmxlKG1vZGVsJHVuaXQuY2xhc3NpZikKcHJpbnQobmIpCmBgYApDb21wcm9iYWNpw7NuIGRlIG5vZG9zIHZhY8Otb3M6CgpgYGB7cn0KZGltX21vZGVsIDwtIDI1KjI1OwpsZW5fbmIgPSBsZW5ndGgobmIpOwplbXB0eV9ub2RlcyA8LSBkaW1fbW9kZWwgIT0gbGVuX25iOwppZiAoZW1wdHlfbm9kZXMpIHsKICBwcmludChwYXN0ZSgiW1dhcm5pbmddIEV4aXN0ZW4gbm9kb3MgdmFjw61vczogIiwgbGVuX25iLCAiLyIsIGRpbV9tb2RlbCkpCn0KYGBgCgojIE1hcGEgZGUgZGlzdGFuY2lhIGVudHJlIHZlY2lub3MKCmBgYHtyfQpwbG90KG1vZGVsLCB0eXBlPSJkaXN0Lm5laWdoYm91cnMiLCBzaGFwZSA9ICJzdHJhaWdodCIpCmBgYAoKIyBJbmZsdWVuY2lhIGRlIGxhcyB2YXJpYWJsZXMKCmBgYHtyfQptb2RlbF9jb2xuYW1lcyA9IGMoImZlY2hhX2NudCIsICJ0bWF4IiwgInRtaW4iLCAicHJlY2lwIikKbW9kZWxfbmNvbCA9IGxlbmd0aChtb2RlbF9jb2xuYW1lcykKYGBgCgojIyBNYXBhIGRlIHZhcmlhYmxlcy4KCmBgYHtyfQpwbG90KG1vZGVsLCBzaGFwZSA9ICJzdHJhaWdodCIpCmBgYAoKIyMgTWFwYSBkZSBjYWxvciBwb3IgdmFyaWFibGUKCmBgYHtyfQpwYXIobWZyb3c9YygzLDQpKQpmb3IgKGogaW4gMTptb2RlbF9uY29sKSB7CiAgcGxvdChtb2RlbCwgdHlwZT0icHJvcGVydHkiLCBwcm9wZXJ0eT1nZXRDb2Rlcyhtb2RlbCwxKVssal0sCiAgICBwYWxldHRlLm5hbWU9bXByLmNvb2xCbHVlSG90UmVkLAogICAgbWFpbj1tb2RlbF9jb2xuYW1lc1tqXSwKICAgIGNleD0wLjUsIHNoYXBlID0gInN0cmFpZ2h0IikKfQpgYGAKCiMjIENvcnJlbGFjacOzbiBwYXJhIGNhZGEgY29sdW1uYSBkZWwgdmVjdG9yIGRlIG5vZG9zCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGNvciA8LSBhcHBseShnZXRDb2Rlcyhtb2RlbCwxKSwgMiwgbXByLndlaWdodGVkLmNvcnJlbGF0aW9uLCB3PW5iLCBzb209bW9kZWwpCiAgcHJpbnQoY29yKQp9CmBgYAoKUmVwcmVzZW50YWNpw7NuIGRlIGNhZGEgdmFyaWFibGUgZW4gdW4gbWFwYSBkZSBmYWN0b3JlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGFyKG1mcm93PWMoMSwxKSkKICBwbG90KGNvclsxLF0sIGNvclsyLF0sIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCB0eXBlPSJuIikKICBsaW5lcyhjKC0xLDEpLGMoMCwwKSkKICBsaW5lcyhjKDAsMCksYygtMSwxKSkKICB0ZXh0KGNvclsxLF0sIGNvclsyLF0sIGxhYmVscz1tb2RlbF9jb2xuYW1lcywgY2V4PTAuNzUpCiAgc3ltYm9scygwLDAsY2lyY2xlcz0xLGluY2hlcz1GLGFkZD1UKQp9CmBgYAoKSW1wb3J0YW5jaWEgZGUgY2FkYSB2YXJpYWJsZSAtIHZhcmlhbnphIHBvbmRlcmFkYSBwb3IgZWwgdGFtYcOxbyBkZSBsYSBjZWxkYToKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgc2lnbWEyIDwtIHNxcnQoYXBwbHkoZ2V0Q29kZXMobW9kZWwsMSksMixmdW5jdGlvbih4LGVmZmVjdGlmKQogICAgIHttPC1zdW0oZWZmZWN0aWYqKHgtd2VpZ2h0ZWQubWVhbih4LGVmZmVjdGlmKSleMikvKHN1bShlZmZlY3RpZiktMSl9LAogICAgIGVmZmVjdGlmPW5iKSkKICBwcmludChzb3J0KHNpZ21hMixkZWNyZWFzaW5nPVQpKQp9CmBgYAoKIyBDbHVzdGVyaW5nCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGhhYyA8LSBtcHIuaGFjKG1vZGVsLCBuYikKfQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDMgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz0zKQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9MykKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA0IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9NCkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTQpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA0LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKYGBgCgojIyMjIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGNhZGEgY2zDunN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcnMuZGltIDwtIGMoZGltKGRmLmNsdXN0ZXIwMSlbMV0sIGRpbShkZi5jbHVzdGVyMDIpWzFdLCBkaW0oZGYuY2x1c3RlcjAzKVsxXSwgZGltKGRmLmNsdXN0ZXIwNClbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIsICJjbHVzdGVyMDQiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA0KQpgYGAKCiMjIyBMb2NhbGl6YWNpw7NuIGdlb2dyw6FmaWNhIGRlIGxhcyBlc3RhY2lvbmVzIGRlIG1lZGlkYSBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQojIEFncnVwYSBwb3IgbG9uZ2l0dWQgeSBsYXRpdHVkIHBhcmEgcmVsbGVuYXIgZWwgbWFwYSBjb24gbWVub3MgZGF0b3MuCmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVyMDEuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMSkKICBkZi5jbHVzdGVyMDIuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMikKICBkZi5jbHVzdGVyMDMuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMykKICBkZi5jbHVzdGVyMDQuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMS5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMi5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMy5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNC5ncm91cGVkKQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDUgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz01KQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9NSkKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQogIGRmLmNsdXN0ZXIwNCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTQpCiAgZGYuY2x1c3RlcjA1IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NSkKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA1IDwtIHNlbGVjdChkZi5jbHVzdGVyMDUsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNSkKYGBgCgojIyMjIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGNhZGEgY2zDunN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcnMuZGltIDwtIGMoZGltKGRmLmNsdXN0ZXIwMSlbMV0sIGRpbShkZi5jbHVzdGVyMDIpWzFdLCBkaW0oZGYuY2x1c3RlcjAzKVsxXSwgZGltKGRmLmNsdXN0ZXIwNClbMV0sIGRpbShkZi5jbHVzdGVyMDUpWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiwgImNsdXN0ZXIwNSIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNSkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA1KQpgYGAKCiMjIyBMb2NhbGl6YWNpw7NuIGdlb2dyw6FmaWNhIGRlIGxhcyBlc3RhY2lvbmVzIGRlIG1lZGlkYSBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQojIEFncnVwYSBwb3IgbG9uZ2l0dWQgeSBsYXRpdHVkIHBhcmEgcmVsbGVuYXIgZWwgbWFwYSBjb24gbWVub3MgZGF0b3MuCmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVyMDEuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMSkKICBkZi5jbHVzdGVyMDIuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMikKICBkZi5jbHVzdGVyMDMuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwMykKICBkZi5jbHVzdGVyMDQuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNCkKICBkZi5jbHVzdGVyMDUuZ3JvdXBlZCA8LSBtcHIuZ3JvdXBfYnlfZ2VvKGRmLmNsdXN0ZXIwNSkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMS5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMi5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwMy5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNC5ncm91cGVkKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuZHJhd19tYXAoc3BhaW4sIGRmLmNsdXN0ZXIwNS5ncm91cGVkKQpgYGAKCiMjIFZpc3VhbGl6YWNpw7NuIGRlIDYgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz02KQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9NikKICBwbG90KG1vZGVsLCB0eXBlPSJtYXBwaW5nIiwKICAgIGJnY29sPWMoInN0ZWVsYmx1ZTEiLCJzaWVubmExIiwieWVsbG93Z3JlZW4iLCJyZWQiLCJibHVlIiwieWVsbG93IiwicHVycGxlIiwiZ3JlZW4iLCJ3aGl0ZSIsIiMxZjc3YjQiLCAnI2ZmN2YwZScsICcjMmNhMDJjJywgJyNkNjI3MjgnLCAnIzk0NjdiZCcsICcjOGM1NjRiJywgJyNlMzc3YzInKVtncm91cHNdLAogICAgc2hhcGUgPSAic3RyYWlnaHQiLCBsYWJlbHMgPSAiIikKICBhZGQuY2x1c3Rlci5ib3VuZGFyaWVzKG1vZGVsLCBjbHVzdGVyaW5nPWdyb3VwcykKfQpgYGAKCiMjIyBBbsOhbGlzaXMgZGUgbGFzIG9ic2VydmFjaW9uZXMgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQXNpZ25hbW9zIGEgY2FkYSByZWdpc3RybyBzdSBjbMO6c3RlcgogIGRmJGNsdXN0ZXIgPC0gZ3JvdXBzW21vZGVsJHVuaXQuY2xhc3NpZl0KfQpgYGAKCk51ZXZvcyBkYXRhZnJhbWVzIHBvciBjbHVzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogICMgQ3JlbyBudWV2b3MgZGF0YWZyYW1lcywgdW5vIHBvciBjYWRhIGNsw7pzdGVyLgogIGRmLmNsdXN0ZXIwMSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTEpCiAgZGYuY2x1c3RlcjAyIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MikKICBkZi5jbHVzdGVyMDMgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0zKQogIGRmLmNsdXN0ZXIwNCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTQpCiAgZGYuY2x1c3RlcjA1IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NSkKICBkZi5jbHVzdGVyMDYgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT02KQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA0LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDUgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA2IDwtIHNlbGVjdChkZi5jbHVzdGVyMDYsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDYpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIsICJjbHVzdGVyMDQiLCAiY2x1c3RlcjA1IiwgImNsdXN0ZXIwNiIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA2KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNikKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA4IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9OCkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTgpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCiAgZGYuY2x1c3RlcjA2IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NikKICBkZi5jbHVzdGVyMDcgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT03KQogIGRmLmNsdXN0ZXIwOCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTgpCgogICMgRXh0cmFpZ28gZGVsIGRhdGFmcmFtZSBsYXMgZmVhdHVyZXMuCiAgZGYuY2x1c3RlcjAxIDwtIHNlbGVjdChkZi5jbHVzdGVyMDEsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMiA8LSBzZWxlY3QoZGYuY2x1c3RlcjAyLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDMgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA0IDwtIHNlbGVjdChkZi5jbHVzdGVyMDQsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA1LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDYgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA3IDwtIHNlbGVjdChkZi5jbHVzdGVyMDcsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwOCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA4LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDgpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0sIGRpbShkZi5jbHVzdGVyMDcpWzFdLCBkaW0oZGYuY2x1c3RlcjA4KVsxXSkKICBiYXJwbG90KGRmLmNsdXN0ZXJzLmRpbSwKICAgICAgICAgIG5hbWVzLmFyZyA9IGMoImNsdXN0ZXIwMSIsICJjbHVzdGVyMDIiLCAiY2x1c3RlcjAzIiwgImNsdXN0ZXIwNCIsICJjbHVzdGVyMDUiLCAiY2x1c3RlcjA2IiwgImNsdXN0ZXIwNyIsICJjbHVzdGVyMDgiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA3KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD01fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwOCkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCiAgZGYuY2x1c3RlcjA3Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDcpCiAgZGYuY2x1c3RlcjA4Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDgpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDcuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDguZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSAxMCBjbMO6c3RlcmVzOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBwbG90KGhhYywgaGFuZz0tMSwgbGFiZWxzPUYpCiAgcmVjdC5oY2x1c3QoaGFjLCBrPTEwKQp9CmBgYAoKIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbMO6c3RlcnMgZW4gZWwgbWFwYQoKQSBxdcOpIGNsw7pzdGVyIHBlcnRlbmVjZSBjYWRhIG5vZG8gZGVsIG1hcGEgZGUga29ob25lbjoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgZ3JvdXBzIDwtIGN1dHJlZShoYWMsIGs9MTApCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCiAgZGYuY2x1c3RlcjA2IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NikKICBkZi5jbHVzdGVyMDcgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT03KQogIGRmLmNsdXN0ZXIwOCA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTgpCiAgZGYuY2x1c3RlcjA5IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09OSkKICBkZi5jbHVzdGVyMTAgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xMCkKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA1IDwtIHNlbGVjdChkZi5jbHVzdGVyMDUsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNiA8LSBzZWxlY3QoZGYuY2x1c3RlcjA2LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDcgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA4IDwtIHNlbGVjdChkZi5jbHVzdGVyMDgsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwOSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA5LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMTAgPC0gc2VsZWN0KGRmLmNsdXN0ZXIxMCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwOSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMTApCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSwgZGltKGRmLmNsdXN0ZXIwNilbMV0sIGRpbShkZi5jbHVzdGVyMDcpWzFdLCBkaW0oZGYuY2x1c3RlcjA4KVsxXSwgZGltKGRmLmNsdXN0ZXIwOSlbMV0sIGRpbShkZi5jbHVzdGVyMTApWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiwgImNsdXN0ZXIwNSIsICJjbHVzdGVyMDYiLCAiY2x1c3RlcjA3IiwgImNsdXN0ZXIwOCIsICJjbHVzdGVyMDkiLCAiY2x1c3RlcjEwIiksCiAgICAgICAgICBjb2wgPSAic3RlZWxibHVlMSIpCn0KYGBgCgojIyMjIERpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zCgpgYGB7ciBmaWcuaGVpZ2h0PTd9CmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDkpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIxMCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA4KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDkpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIxMCkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCiAgZGYuY2x1c3RlcjA2Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDYpCiAgZGYuY2x1c3RlcjA3Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDcpCiAgZGYuY2x1c3RlcjA4Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDgpCiAgZGYuY2x1c3RlcjA5Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDkpCiAgZGYuY2x1c3RlcjEwLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMTApCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDYuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDcuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDguZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDkuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMTAuZ3JvdXBlZCkKYGBgCg==